Skip to content

Refactor unified_qr.rs to use bitcoin-payment-instructions #607

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

chuksys
Copy link

@chuksys chuksys commented Aug 11, 2025

This PR introduces a unified.rs module (which is a refactor of the unified_qr.rs module) - this refactor allows us to use this module as a single API for sending payments to BIP 21/321 URIs as well as BIP 353 HRNs, creating a simpler interface for users.

https://github.com/rust-bitcoin/bitcoin-payment-instructions is used to parse BIP 21/321 URIs as well as the BIP 353 HRNs.

Changes

  • The unified_qr.rs module has been renamed to unified.rs.
  • The UnifiedQrPayment struct has been renamed to UnifiedPayment.
  • The QRPaymentResult enum has been renamed to PaymentResult.
  • The send method in unified.rs now supports sending to both BIP 21/321 URIs as well as BIP 353 HRNs.
  • The integration tests for the module have been updated accordingly.

I will follow-up with adding an end-to-end test that asserts that sending to HRNs using this API works.

This PR closes #521

This rename reflects that this module is a unified payment interface for both QR code payments and HRN payments passed in as a string without scanning a QR code
…PaymentResult

These renamings are necessary to reflect the expanded responsibilities for this module.
This commit adds a HRN Resolver to the Node struct which will be useful for resolving HRNs when making BIP 353 payments
This commit refactors the integration tests for unified_payment - specifically qr_uri (bip21) generation and sending to qr_uri (bip21)
@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Aug 11, 2025

I've assigned @joostjager as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@chuksys chuksys marked this pull request as draft August 11, 2025 18:25
@tnull tnull self-requested a review August 13, 2025 07:57
Copy link
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, thanks! Did a first ~high-level round. Feel free to undraft this.

@@ -304,7 +304,7 @@ impl DeserializationError for Extras {
#[cfg(test)]
mod tests {
use super::*;
use crate::payment::unified_qr::Extras;
use crate::payment::unified::Extras;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think we can just drop this line as the super::* import above covers Extras. Although, it would actually be preferable to switch to explicit import here, i.e., use super::{Extras, ...}.

@@ -952,8 +952,8 @@ impl Node {
/// [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md
/// [BIP 21]: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki
#[cfg(feature = "uniffi")]
pub fn unified_qr_payment(&self) -> Arc<UnifiedQrPayment> {
Copy link
Collaborator

@tnull tnull Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind updating the docs here and above that this will now also be used for HRNs?

@@ -189,7 +189,7 @@ impl UnifiedQrPayment {
/// [`PaymentId`]: lightning::ln::channelmanager::PaymentId
/// [`Txid`]: bitcoin::hash_types::Txid
#[derive(Debug)]
pub enum QrPaymentResult {
pub enum PaymentResult {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe UnifiedPaymentResult then?

@@ -91,6 +91,7 @@ log = { version = "0.4.22", default-features = false, features = ["std"]}

vss-client = "0.3"
prost = { version = "0.11.6", default-features = false}
bitcoin-payment-instructions = { git = "https://github.com/rust-bitcoin/bitcoin-payment-instructions" }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, @TheBlueMatt, can we get a bitcoin-payment-instructions release that includes rust-bitcoin/bitcoin-payment-instructions#6 ?

@chuksys In the meantime, please import a specific commit via rev = "..", as otherwise our builds might break as soon as bitcoin-payment-instructions's main changes.

@@ -1633,6 +1635,14 @@ fn build_with_store_internal(
let (stop_sender, _) = tokio::sync::watch::channel(());
let background_processor_task = Mutex::new(None);

let hrn_resolver = Arc::new(LDKOnionMessageDNSSECHrnResolver::new(network_graph.clone()));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Please use Arc::clone(&..) here and everywhere for Arcs. This helps distinguishing that we in fact do not clone the actual value here.

amount::Amount as BPIAmount, PaymentInstructions, PaymentMethod,
};

use crate::types::HRNResolver;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Let's move this up to the other crate:: imports.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please include the test changes in the corresponding commit(s) that require them. Each commit should build and test individually, not just the whole PR.

Ok(payment_id) => return Ok(QrPaymentResult::Bolt12 { payment_id }),
Err(e) => log_error!(self.logger, "Failed to send BOLT12 offer: {:?}. This is part of a unified QR code payment. Falling back to the BOLT11 invoice.", e),
/// [BIP 353]: https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki
pub async fn send(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is the first async method (mod Node::next_event_async) we do expose in the API, which makes it a bit incongruent with the rest of the API.

That said, I'm not super opposed to it, let's use this as a first experimental venture into async-API world. If any users complain, we could reuse our runtime to drive this and expose a blocking API, which is what we did so far.

pub async fn send(
&self, uri_str: &str, amount_msat: Option<u64>,
) -> Result<PaymentResult, Error> {
let instructions = match PaymentInstructions::parse(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than matching, let's just use .map_err(|e| .. )?, which should make this more readable and less vertical.

Also possible for ~most other matches below.

return Err(Error::InvalidAmount);
}
},
PaymentInstructions::FixedAmount(ref instr) => instr.clone(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we at least check that the amount_msat (if provided) is greater or equal to the fixed amount?

Copy link
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a rebase by now, also.

@chuksys chuksys marked this pull request as ready for review August 17, 2025 18:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Switch to use bitcoin-payment-instructions
3 participants